package cl.ucn.disc.biblio.refcluster.reference;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.text.ParseException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;

import org.apache.commons.lang.StringUtils;

import cl.ucn.disc.biblio.refcluster.util.MultiMap;

public class BiblioRecord {

	private static final String END_OF_RECORD = "ER";
	private static final String EMPTY_HEADER = "   ";
	MultiMap<String, String> contents = new MultiMap<String, String>();
	String string;
	static final String REFERENCES = "CR";
	static final String AUTHORS = "AU";
	public static final String ID = "UT";
	public static final String PUBLICATION_TYPE = "PT";
	
	public static java.util.Iterator<BiblioRecord> getIterator(Scanner s) {
		return new BiblioRecordScannerIterator(s);
	}
	
	private static class BiblioRecordScannerIterator implements Iterator<BiblioRecord> {

		private Scanner scanner;

		public BiblioRecordScannerIterator(Scanner s) {
			scanner = s;
			// Skips first two lines
			if (s.hasNextLine()) {
				s.nextLine();
			}
			if (s.hasNextLine()) {
				s.nextLine();
			}
		}
		
		@Override
		public boolean hasNext() {
			return scanner.hasNextLine();
		}

		@Override
		public BiblioRecord next() {
			try {
				return new BiblioRecord(scanner);
			} catch (ParseException e) {
				e.printStackTrace();
				return null;
			}
		}

		@Override
		public void remove() {
			throw new UnsupportedOperationException();
			
		}
		
	}
	

	private BiblioRecord(Scanner s) throws ParseException {

		read(s);


	}
	
	public boolean isHeader() {
		return !contents.containsKey(PUBLICATION_TYPE);
	}

	public void read(Scanner s) {
		String currentHeader = "OTHER";

		while (s.hasNextLine()) {
			String line = s.nextLine();
			if (line.trim().length() == 0) {
				return;
			} else {
				if (line.length() >= 2) {
					String header = line.substring(0, 2);
					if (!headerEmpty(header)) {
						currentHeader = header; 
					}
					
					if (currentHeader.equals("VR")) { 
						// Creates a BiblioRecord containing the header information of the WoSFile
						contents.add(currentHeader, line.substring(3));
						return;
					}

					
					// This is a fix for a problem in WoS files: The DOI in references is in the next line. 
					if (currentHeader.equals("CR") && line.trim().endsWith("DOI")) { 
						// Concatenates the DOI if it is in the next line
						line = line.concat(" ").concat(s.nextLine());
					}
					if (line.length() > 2) {
						contents.add(currentHeader, line.substring(3));
					} else {
						contents.add(currentHeader, "");
					}
				}

			}

		}
	}

	public Set<String> getFieldSet(String fieldName) {
		String key = fieldName;
		return contents.get(key);
	}
	
	public String getFirstFromFieldSet(String fieldName) {
		Set<String> fs = getFieldSet(fieldName);
		if (fs != null && ! fs.isEmpty()) {
			return fs.iterator().next();
		} else {
			return null;
		}
	}
	
	public void setField(String fieldName, Set<String> value) {
		contents.put(fieldName, value);
	}
	
	private boolean headerEmpty(String header) {
		return header.trim().length() == 0;
	}

	public void write(PrintWriter out) {
		for (String header : contents.keySet()) {
			if (!contents.get(header).isEmpty()) {
				Iterator<String> it = contents.get(header).iterator();
				String value = it.next();
				if (header.equals(END_OF_RECORD)) {
					out.println(END_OF_RECORD);
					out.println();
					return;
				} else {
					out.println(StringUtils.rightPad(header, 3) + value);
				}
				while (it.hasNext()) {
					value = it.next();
					out.println(EMPTY_HEADER + value);
				}
			}
		}
	}

	public static void writeBiblioRecords(String outputFile, BiblioRecord header, Iterator<BiblioRecord> it) throws FileNotFoundException {
		PrintWriter w = new PrintWriter(outputFile);
		w.println("FN ISI Export Format\nVR 1.0");
		
		if (header != null) {
			header.write(w);
		}

		while(it.hasNext()) {
			it.next().write(w);
		}
		w.close();
		BiblioRecord.closeIterator(it);
	}

	public static Iterator<BiblioRecord> getIterator(Set<String> biblioRecordIdSet, String... wosFiles) throws FileNotFoundException {
		Map<String, BiblioRecord> brMap = new HashMap<String,BiblioRecord>();
		for (int i=wosFiles.length-1; i>=0; i--) {
			brMap.putAll(getMap(wosFiles[i]));
		}
		brMap.keySet().retainAll(biblioRecordIdSet);
		return brMap.values().iterator();
		
	}
	
	public static void closeIterator(Iterator<BiblioRecord> it) {
		if (it instanceof BiblioRecordScannerIterator) {
			((BiblioRecordScannerIterator)it).scanner.close();
		}
	}

	public static Map<String,BiblioRecord> getMap(String wosFile) throws FileNotFoundException {
		Map<String,BiblioRecord> brMap = new HashMap<String, BiblioRecord>();
		Iterator<BiblioRecord> it = getIterator(new Scanner(new File(wosFile)));
		while (it.hasNext()) {
			BiblioRecord br = it.next();
			String id = br.getFirstFromFieldSet(BiblioRecord.ID);
			if (id != null) {
				brMap.put(id, br);
			}
		}
		return brMap;
	}


}
